﻿using System;
using System.Collections.Generic;
using System.Linq;

using System.Text;

namespace MathLib {

  public  class PopulationModeling {
        private double maxFertility;
        private int liveLength;
        public int Period {private set; get;}
        public int groupCount;
        public int populationsCount;
        private double popValue;
      public Matrix[] P;
      public Matrix[] Noise;
      public Matrix[] PopulationMatrixes { private set; get; }



//Конструктор, который вызывается при моделировани популяции с нуля
        public PopulationModeling(double maxFertility, int groupCount, int populationsCount, double popValue) {
            if (populationsCount <= 1 || groupCount <= 0)
                throw new Exception("Неверно заданы параметры");
            this.liveLength = 100;
            this.maxFertility = maxFertility;
            this.groupCount = groupCount;
            this.Period = liveLength / groupCount;
            this.populationsCount = populationsCount;
            this.popValue = popValue;
            this.P = new Matrix[populationsCount];
            this.P[0] = GenerateInit(popValue);
           
        }

//Констурктор, который вызывается при моделировании популяции зная начальное состояние
        public PopulationModeling(double maxFertility, int populationsCount, Matrix x) {
            double sum = 0;
            this.liveLength = 100;
            this.groupCount = x.NoRows;
            this.maxFertility = maxFertility;
            this.Period = liveLength / groupCount;
            this.populationsCount = populationsCount;
            for (int i = 0; i <= groupCount - 1; i++) {
                sum += x[i,0];
            } 
            this.popValue = sum/groupCount;
            this.P = new Matrix[populationsCount];
            this.P[0] = x;

        }

        public PopulationModeling( int groupCount) {
                    this.groupCount = groupCount;
        }


        public Matrix GenerateA() {
            Matrix f = GenerateFertilityRate();
            Matrix m = GenerateMortalityRate();
            
            MatrixA A = new MatrixA(groupCount, f, m);

            return A;
        }


      //Генерирует последовательность векторов состояний популяции
        public void GeneratePopulation() {

            PopulationMatrixes = new Matrix[populationsCount];

            for (int i = 0; i < populationsCount; i++) {
                Matrix A = GenerateA();
                P[i + 1] = A * P[i];
                PopulationMatrixes[i] = A;
            }
            for (int i = 0; i < populationsCount - 1; i++) {
                Matrix A = GenerateA();
                P[i + 1] = A * P[i];
                PopulationMatrixes[i] = A;
            }

        }

      // Генерирует первый вектор состояния
        public Matrix GenerateInit(double last) {
            Gauss g = new Gauss();
            Matrix v = new Matrix(groupCount, 1);
            int first = 0;
            double value;
            double h = (last - first)/groupCount;
            double step = 0;
            for (int i = 0; i < groupCount; i++) {
                value = g.NextGaussian(0, last / 100);
                v[i, 0] = Math.Round(-(step + value) * (step + value) + (last + value) * (last + value));
                step += h;
            }
            return v;
        }


        public  Matrix GenerateMigrationRate() {
            Gauss g = new Gauss();
            Matrix v = new Matrix(groupCount, 1);
            double value;
            for (int i = 0; i < groupCount; i++) {
                value = g.NextGaussian(0, popValue);
                    v[i, 0] = Math.Round(value);

            }
            return v;
        }

        private  Matrix GenerateFertilityRate() {
            Gauss g = new Gauss();
            Matrix v = new Matrix(groupCount, 1);
            int first = -groupCount;
            int last = groupCount;
            double h = (last - first) / groupCount;
            double value;
            double x = first;
            for (int i = 0;  i < groupCount; i++)
            {
             value = g.NextGaussian(0, last/100);
            v[i,0] = Math.Round(maxFertility*Math.Exp(-(x*x)/10)+ Math.Abs(value), 4);
            x = x + h;
            }
            return v;
        }

      //Доработать
        private Matrix GenerateMortalityRate() {
            Gauss g = new Gauss();
            Matrix v = new Matrix(groupCount - 1, 1);
            double value;
             for (int i = 0;  i < groupCount - 1; i++)
             {
             value = g.NextGaussian(0, 0.0001);
             v[i, 0] = Math.Round(1 - 0.112 * Math.Sqrt(i) - Math.Abs(value), 4);
             }
           //  v[groupCount-1, 0] = 0.72;
            return v;
        }

    }

}